/**
 * ZVue 编译器
 *
 * 1.获取并遍历DOM树
 * 2.文本节点：获取{{}}格式的内容并解析
 * 3.元素节点：访问节点特性，截获k-或@开头的内容并解析
 * */

class Compile {
    constructor(el, vm){
        this.$vm = vm;
        this.$el = document.querySelector(el);

        if(this.$el){
            //执行编译
            this.compile(this.$el)
        }
    }

    /**
     * 编译
     * */
    compile(el){
        const childNodes = el.childNodes;
        Array.from(childNodes).forEach(node => {
            //判断节点类型
            if(this.isElement(node)){
                this.compileElement(node)
            }else if(this.isInter(node)){
                //文本节点 {{xxx}}
                this.compileText(node)
            }

            //递归遍历
            if(node.childNodes && node.childNodes.length > 0){
                this.compile(node);
            }
        })
    }

    /**
     * 判断是否为元素节点
     * */
    isElement(node){
        return node.nodeType === 1;
    }

    /**
     * 判断是否为文本节点
     * */
    isInter(node){
        return node.nodeType === 3 && /{{(.*)}}/.test(node.textContent)
    }

    /**
     * 更新
     * */
    update(node, exp, dir){
        //构造更新函数并执行,相当于首次赋值
        let updateFn  = this[dir + 'Update'];
        updateFn && updateFn.call(this,node, this.$vm[exp], exp);

        //创建一个 Watcher，执行后续的更新
        //传递一个更=跟新函数，能够更新指定的dom元素
        new Watcher(this.$vm, exp, function(value){
            updateFn && updateFn(node, value);

        })
    }


    /**
     * 编译文本
     * */
    compileText(node){
        this.update(node, RegExp.$1, 'text')
    }
    //文本更新
    textUpdate(node, value, exp){
        node.textContent = value
    }


    /**
     * 节点编译
     * */
    compileElement(node){
        let nodeAttrs = node.attributes;

        Array.from(nodeAttrs).forEach(attr => {
            const name = attr.name; // k-text
            const exp = attr.value; // name

            //判断指令
            if(name.indexOf('z-') === 0){
                const dir = name.substring(2); // text
                this.update(node, exp, dir); //执行更新
            }

            //判断事件监听
            if(name.indexOf('@') === 0){
                const dir = name.substring(1); // click
                this.eventHandle(node, exp, dir)
            }
        })
    }

    /**
     * 双向绑定
     * */
    modelUpdate(node, value, exp){
        let _this = this;
        let nodeName = node.nodeName;
        if(nodeName === 'INPUT' || nodeName === 'TEXTAREA'){
            node.value = value;
            node.addEventListener('input',function(){
                let val = node.value;
                if(_this && _this.$vm){
                    _this.$vm[exp] = val;
                }
            })
        }else{
            node.textContent = value
        }
    }

    /**
     * 事件绑定
     * */
    eventHandle(node, exp, dir){
        const fn = this.$vm.$options.methods && this.$vm.$options.methods[exp];

        if(dir && fn){
            node.addEventListener(dir, fn.bind(this.$vm))
        }
    }
}









